package com.agileboot.domain.examrecord.db;

import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.utils.CertificateUtil.ContentStyle;
import com.agileboot.common.utils.CertificateUtil.DateTimeUtil;
import com.agileboot.common.utils.CertificateUtil.PDFUtil;
import com.agileboot.common.utils.CommonUtils;
import com.agileboot.domain.SaltEncryption;
import com.agileboot.domain.answer.db.Answer;
import com.agileboot.domain.answer.db.AnswerMapper;
import com.agileboot.domain.answer.vo.AnswerVO;
import com.agileboot.domain.exam.db.Exam;
import com.agileboot.domain.exam.db.ExamMapper;
import com.agileboot.domain.examquestion.db.ExamQuestion;
import com.agileboot.domain.examquestion.db.ExamQuestionMapper;
import com.agileboot.domain.examrecord.query.ExamRecordQuery;
import com.agileboot.domain.examrecord.vo.ExamRecordInfoVO;
import com.agileboot.domain.examrecord.vo.ExamRecordVO;
import com.agileboot.domain.question.db.Question;
import com.agileboot.domain.question.db.QuestionMapper;
import com.agileboot.domain.system.user.db.SysUserEntity;
import com.agileboot.domain.system.user.db.SysUserMapper;
import com.agileboot.infrastructure.cache.RedisUtil;
import com.agileboot.infrastructure.user.AuthenticationUtils;
import com.agileboot.infrastructure.user.web.SystemLoginUser;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author lx
 * @implNote 2022/2/26
 */
@Slf4j
@Service
public class ExamRecordServiceImpl extends ServiceImpl<ExamRecordMapper, ExamRecord> implements ExamRecordService {
    @Autowired
    private SysUserMapper userMapper;
    @Autowired
    private ExamRecordMapper examRecordMapper;
    @Autowired
    private ExamQuestionMapper examQuestionMapper;
    @Autowired
    private AnswerMapper answerMapper;
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public PageDTO<ExamRecord> getUserGrade(String username, Integer examId, Integer pageNo, Integer pageSize) {
        SysUserEntity user = Optional.ofNullable(userMapper.selectOne(new QueryWrapper<SysUserEntity>().eq("username", username)))
                .orElseThrow(() -> new ApiException(ErrorCode.Business.USER_ADMIN_CAN_NOT_BE_MODIFY));

        QueryWrapper<ExamRecord> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", user.getUserId());
        CommonUtils.setEqualsQueryWrapper(wrapper, Collections.singletonMap("exam_id", examId));

        IPage<ExamRecord> page = examRecordMapper.selectPage(new Page<>(pageNo, pageSize), wrapper);

        return new PageDTO<ExamRecord>(page.getRecords(),page.getTotal());
    }

    //    @Cache(prefix = "exam:record", suffix = "#recordId", ttl = 10, randomTime = 2, timeUnit = TimeUnit.HOURS)
    @Override
    public ExamRecord getExamRecordById(Integer recordId) {
        return examRecordMapper.selectById(recordId);
    }

    @Override
    public void createExamCertificate(HttpServletResponse response, String examName, Integer examRecordId) {
        //  1. 查询考试记录信息
        ExamRecord examRecord = getExamRecordById(examRecordId);
        //  2. 查询用户的真实姓名生成证书
        SystemLoginUser user = AuthenticationUtils.getSystemLoginUser();
//        User user = Optional.ofNullable(userMapper.selectById(examRecord.getUserId())).orElse(User.builder().trueName("该用户已注销").build());

        //  ****windows下用如下路径****
        //  获取证书背景图片路径
        String backgroundImage = Objects.requireNonNull(PDFUtil.class.getClassLoader().getResource("static/images/certificateBg.png")).getPath();
        //  获取发放证书的项目Logo
        String logo = Objects.requireNonNull(PDFUtil.class.getClassLoader().getResource("static/images/logo.png")).getPath();
        //  生成的pdf的文件位置(一个模板多次生成)
        String pdfFilePath = Objects.requireNonNull(PDFUtil.class.getClassLoader().getResource("static/templateCertificate.pdf")).getPath();


        //  ****linux服务器下用如下(地址对应服务器上文件路径)
//         String backgroundImage = "/exam/images/certificateBg.png";
//         String logo = "/exam/images/logo.png";
//         String pdfFilePath = "/exam/templateCertificate.pdf";

        //  生成工具类
        PDFUtil pdfUtil = new PDFUtil();

        //  证书字体样式
        ContentStyle style1 = new ContentStyle();
        style1.setFontSize(15);
        ContentStyle style2 = new ContentStyle();
        style2.setFontSize(10);

        //  准备证书所需要的数据
        String trueName = user.getUsername();
        Date examTime = examRecord.getExamTime();
        //  生成XXX同学信息
        String userInfo = trueName + "同学：";
        //  生成证书内容
        String content = "您于" + DateTimeUtil.DateToString(examTime) + "在" + examName + "测评中取得优异成绩!";
        //  创建证书
        try {
            pdfUtil.openDocument(pdfFilePath).addImage(backgroundImage, 0, 400).addLogo(logo, 270, 480).addContent(userInfo, 85, 630, style1).addContent("特发此证,以资鼓励!", 125, 495, style2).addContent("Power By WangZhouzhou", 360, 495, style2);
            //  结束截取字符串的索引
            int end;
            //  证书内容分行,防止超出证书边缘
            for (int i = 0, y = 590; i < content.length(); y -= 30) {
                end = Math.min(i + 30, content.length());
                pdfUtil.addContent(content.substring(i, end), 125, y, style1);
                i = end;
            }
        } catch (Exception e) {
            log.error("生成证书错误: " + e);
        }
        //  关闭创建pdf的工具
        pdfUtil.close();
        //  文件转码
        if (pdfFilePath.contains("%")) {
            try {
                pdfFilePath = URLDecoder.decode(pdfFilePath, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        //  输出流
        ServletOutputStream out = null;
        FileInputStream in = null;
        try {
            in = new FileInputStream(pdfFilePath);
            String[] dir = pdfFilePath.split("/");
            //  获取文件名
            String fileName = dir[dir.length - 1];
            String[] array = fileName.split("[.]");
            //  文件类型
            String fileType = array[array.length - 1].toLowerCase();
            // 设置文件ContentType类型
            if ("jpg,jepg,gif,png".contains(fileType)) {// 图片类型
                response.setContentType("image/" + fileType);
            } else if ("pdf".contains(fileType)) {// pdf类型
                response.setContentType("application/pdf");
            } else {// 自动判断下载文件类型
                response.setContentType("multipart/form-data");
            }
            // 设置文件头：最后一个参数是设置下载文件名
            // response.setHeader("Content-Disposition", "attachment;fileName="+fileName);
            out = response.getOutputStream();
            //  读取文件流
            int len;
            byte[] buffer = new byte[1024 * 10];
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            out.flush();
        } catch (FileNotFoundException e) {
            log.error("responseFileStream error:FileNotFoundException" + e);
        } catch (Exception e) {
            log.error("responseFileStream error:" + e);
        } finally {
            try {
                assert out != null;
                out.close();
                in.close();
            } catch (NullPointerException e) {
                log.error("responseFileStream stream close() error:NullPointerException" + e);
            } catch (Exception e) {
                log.error("responseFileStream stream close() error:" + e);
            }
        }
    }

    @Override
    public Integer addExamRecord(ExamRecord examRecord, HttpServletRequest request) {
        // 当前用户对象的信息
        SystemLoginUser systemLoginUser = AuthenticationUtils.getSystemLoginUser();
        SysUserEntity user = userMapper.selectOne(new QueryWrapper<SysUserEntity>().eq("username", systemLoginUser.getUsername()));
        // 设置考试信息的字段
        examRecord.setUserId(systemLoginUser.getUserId());
        // 设置id
        List<ExamRecord> examRecords = examRecordMapper.selectList(null);
        int id = 1;
        if (examRecords.size() > 0) {
            id = examRecords.get(examRecords.size() - 1).getRecordId() + 1;
        }
        examRecord.setRecordId(id);

        // 设置逻辑题目的分数
        // 查询所有的题目答案信息
        List<Answer> answers = answerMapper.selectList(new QueryWrapper<Answer>().in("question_id", Arrays.asList(examRecord.getQuestionIds().split(","))));
        // 查询考试的题目的分数
        HashMap<String, String> map = new HashMap<>();// key是题目的id  value是题目分值
        ExamQuestion examQuestion = examQuestionMapper.selectOne(new QueryWrapper<ExamQuestion>().eq("exam_id", examRecord.getExamId()));
        // 题目的id
        String[] ids = examQuestion.getQuestionIds().split(",");
        // 题目在考试中对应的分数
        String[] scores = examQuestion.getScores().split(",");
        for (int i = 0; i < ids.length; i++) {
            map.put(ids[i], scores[i]);
        }
        // 逻辑分数
        int logicScore = 0;
        // 错题的id
        StringBuilder sf = new StringBuilder();
        // 用户的答案
        String[] userAnswers = examRecord.getUserAnswers().split("-");
        for (int i = 0; i < examRecord.getQuestionIds().split(",").length; i++) {
            int index = SaltEncryption.getIndex(answers, Integer.parseInt(examRecord.getQuestionIds().split(",")[i]));
            if (index != -1) {
                String multiTrueOptions = Arrays.stream(answers.get(index).getTrueOption().replace("[", "").replace("]", "").split(","))
                        .map(String::trim) // 在这里添加 trim() 方法
                        .filter(str -> !str.isEmpty()) // 过滤掉空字符串
                        .collect(Collectors.joining(","));
                if (multiTrueOptions.contains(userAnswers[i])){
//                }
//                if (Objects.equals(userAnswers[i], answers.get(index).getTrueOption())) {
                    logicScore += Integer.parseInt(map.get(examRecord.getQuestionIds().split(",")[i]));
                } else {
                    sf.append(examRecord.getQuestionIds().split(",")[i]).append(",");
                }
            }
        }
        examRecord.setLogicScore(logicScore);
        if (sf.length() > 0) {// 存在错的逻辑题
            examRecord.setErrorQuestionIds(sf.substring(0, sf.toString().length() - 1));
        }

        System.out.println(examRecord);
        examRecord.setExamTime(new Date());
        examRecordMapper.insert(examRecord);
        return id;
    }

    @Override
    public PageDTO<ExamRecord> getExamRecord(Integer examId, Integer pageNo, Integer pageSize) {
        QueryWrapper<ExamRecord> wrapper = new QueryWrapper<>();
        CommonUtils.setEqualsQueryWrapper(wrapper, Collections.singletonMap("exam_id", examId));

        IPage<ExamRecord> page = examRecordMapper.selectPage(new Page<>(pageNo, pageSize), wrapper);

        return new PageDTO<>(page.getRecords());
    }

    @Override
    public void setObjectQuestionScore(Integer totalScore, Integer examRecordId) {
        ExamRecord examRecord = examRecordMapper.selectOne(new QueryWrapper<ExamRecord>().eq("record_id", examRecordId));
        examRecord.setTotalScore(totalScore);
        examRecordMapper.update(examRecord, new UpdateWrapper<ExamRecord>().eq("record_id", examRecordId));
//        redisUtil.del("exam:record:" + examRecordId);
    }

    @Override
    public PageDTO<ExamRecordVO> getRecordExam(ExamRecordQuery query) {
        Page<ExamRecord> examRecordPage = examRecordMapper.selectPage(query.toPage(), query.toQueryWrapper());
        List<ExamRecord> records = examRecordPage.getRecords();
        ArrayList<ExamRecordVO> list = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(records)){
            for (ExamRecord record : records) {
                ExamRecordVO examRecordVO = new ExamRecordVO();
                BeanUtils.copyProperties(record,examRecordVO);
                SysUserEntity userEntity = userMapper.selectById(record.getUserId());
                if (Objects.nonNull(userEntity)){
                    examRecordVO.setUserName(userEntity.getUsername());
                }
                Exam exam = examMapper.selectById(record.getExamId());
                if (Objects.nonNull(exam)){
                    examRecordVO.setExamName(exam.getExamName());
                }
                list.add(examRecordVO);
            }
        }
        return new PageDTO<ExamRecordVO>(list,examRecordPage.getTotal());
    }

    @Override
    public List<ExamRecordInfoVO> getRecordExamDetail(Integer id) {
        ArrayList<ExamRecordInfoVO> examRecordInfoVOS = new ArrayList<>();
        //先通过用户考试的试卷id查询试卷
        ExamRecord examRecord = baseMapper.selectById(id);
        //用户的答案
        String[] userAnswer = examRecord.getUserAnswers().split("-");
        String questionIds = examRecord.getQuestionIds();
        //该试卷所有问题的id
        String[] questionIdList = questionIds.split(",");
        //遍历id，找到问题的答案，类型，分数，
        for (int i = 0; i < questionIdList.length; i++) {
            String questionId = questionIdList[i];
            ExamRecordInfoVO examRecordInfoVO = new ExamRecordInfoVO();
            examRecordInfoVO.setQuestionId(questionId);
            //用户回答的答案
            examRecordInfoVO.setUserAnswer(userAnswer[i]);
            //查询问题的类型，问题的名称
            Question question = questionMapper.selectById(questionId);
            if (Objects.nonNull(question)){
                examRecordInfoVO.setQuType(question.getQuType());
                examRecordInfoVO.setQuContent(question.getQuContent());
            }
            //问题的答案
            getQuestionAnswer(questionId, examRecordInfoVO);
            examRecordInfoVOS.add(examRecordInfoVO);
        }
        if(!CollectionUtils.isEmpty(examRecordInfoVOS)) return examRecordInfoVOS.stream().sorted(Comparator.comparing(ExamRecordInfoVO::getQuType)).collect(Collectors.toList());
        return Collections.emptyList();
    }

    private void getQuestionAnswer(String questionId, ExamRecordInfoVO examRecordInfoVO) {
        LambdaQueryWrapper<Answer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Answer::getQuestionId, questionId);
        Answer answer = answerMapper.selectOne(queryWrapper);
        ArrayList<AnswerVO> list = new ArrayList<>();
        if (Objects.nonNull(answer)) {
            String trueOption = answer.getTrueOption();
            List<String> options = Arrays.asList(answer.getAllOption().split(","));
            List<String> analysis = Arrays.asList(answer.getAnalysis().replace("[", "").replace("]", "").split(","));
            List<String> img = Arrays.asList(answer.getImages().replace("[", "").replace("]", "").split(","));
            for (int i = 0; i < options.size(); i++) {
                AnswerVO res = new AnswerVO();
                //id 选项 正确选项 ，img ，解析
                res.setId(i);
                res.setAnswer(options.get(i));
                res.setIsTrue(trueOption.contains(String.valueOf(i)));
                res.setImages(img.get(i));
                res.setAnalysis(analysis.get(i));
                list.add(res);
            }
            examRecordInfoVO.setAnswerVOList(list);
        }
    }

}
